package exabase.com;

import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import exabase.com.dataTransfer.DataTransfer;
import exabase.com.dataTransfer.PeerInfo;

public class RequestProcessor implements Runnable {
	private static Logger logger = Logger.getLogger(RequestProcessor.class
			.getName());

	private static List<Socket> pool = new LinkedList<Socket>();

	public RequestProcessor() {
	}

	public static void processRequest(Socket request) {
		synchronized (pool) {
			pool.add(pool.size(), request);
			pool.notifyAll();
		}
	}

	public void run() {
		while (true) {
			synchronized (pool) {
				while (pool.isEmpty()) {
					try {
						pool.wait();
					} catch (InterruptedException ex) {
						logger.log(Level.INFO, "interrupted", ex);
					}
				}
				socket = (Socket) pool.remove(0);
				logger.fine("got connection: " + socket);
			}
			handleConnection();
		}
	}

	protected Socket socket;
	protected DataTransfer dataTransfer;

	public DataTransfer getDataTransfer() {
		return dataTransfer;
	}

	private boolean openSocketForDataTransfer() {
		try {
			dataTransfer = new DataTransfer(socket);
		} catch (IOException e) {
			logger.log(Level.WARNING,
					"Could not open socket for data transfers", e);
			return false;
		}
		return true;
	}

	protected void handleConnection() {
		openSocketForDataTransfer();
		processRequests();
	}

	protected void processRequests() {
		while (socket.isConnected()) {
			try {
				Request request = dataTransfer.getRequest(1000);
				logger.info("got request: " + request.toString());

				switch (request.getRequest()) {
				case SEND_AND_GET_PEER_INFO:
					handleGetAndSendPeerInfo(request);
					break;
				// case SEND_DATA_WITH_ACK:
				// receiveDataFile();
				// break;
				// case SEND_META:
				// receiveMetaFile();
				// break;
				// case SEND_I_PING:
				// receivePing();
				// break;
				// case GET_DATA:
				// processDataRequest();
				// break;
				case BYE:
					dataTransfer.disconnectNow();
					return;
				default:
					logger.warning("Unknown request");
					break;
				}
			} catch (EOFException e) {
				logger.log(Level.FINER, "Socket closed.", e);
				break;
			} catch (SocketTimeoutException e) {
				// Read timed out. Do nothing.
			} catch (SocketException e) {
				// Connection probably just reset. Do nothing.
			} catch (Exception e) {
				logger.log(Level.WARNING, "Error", e);
				break;
			}
		}
	}

	//
	// private void processDataRequest() throws IOException,
	// ClassNotFoundException {
	// Hash hash = dataTransfer.receiveHash();
	// Data data = DataStore.getInstance().getData(hash);
	// if (data != null)
	// dataTransfer.sendData(data);
	// else
	// dataTransfer.sendRequest(RequestEnum.SORRY);
	// }
	//
	// private void receivePing() throws IOException, ClassNotFoundException {
	// int ping = dataTransfer.recievePing();
	// logger.finer("receive ping " + ping);
	// dataTransfer.sendPing(ping);
	// }
	//
	// private void receiveDataFile() throws IOException {
	// File file = dataTransfer.receiveFile();
	// Hash hash = DataStore.getInstance().addFile(file.getAbsolutePath());
	// dataTransfer.sendHash(hash);
	// }
	//
	// private void receiveMetaFile() throws IOException, ClassNotFoundException
	// {
	// Meta meta = dataTransfer.receiveMeta();
	// MetaStore.getInstance().addMeta(meta);
	// }

	private void handleGetAndSendPeerInfo(Request request) throws IOException,
			ClassNotFoundException {
		Request ack = new Request(RequestEnum.ACK, request.getId());
		logger.info("sending ack: " + ack);
		dataTransfer.sendRequest(ack);
		logger.info("trying to get peerInfo");
		PeerInfo peerInfo = (PeerInfo) dataTransfer.readObject();
		logger.info("got peerInfo: " + peerInfo);
		List<PeerInfo> list = new ArrayList<PeerInfo>();
		logger.info("Trying to send list: " + list);
		dataTransfer.sendObject(list);
		logger.info("Sent list. Done!");
	}
}
